package com.kmxd.ams.app.system;

import static com.kmxd.ams.core.common.constant.RoleConst.ROLE_SYS_ADMIN_CODE;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kmxd.ams.client.system.*;
import com.kmxd.ams.client.system.dto.*;
import com.kmxd.ams.client.system.vo.*;
import com.kmxd.ams.core.common.bo.LoginUser;
import com.kmxd.ams.core.common.constant.CacheConst;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.common.enums.DataScopeTypeEnum;
import com.kmxd.ams.core.common.enums.DelFlagEnum;
import com.kmxd.ams.core.common.enums.StatusEnum;
import com.kmxd.ams.core.common.enums.YesNoEnum;
import com.kmxd.ams.core.common.query.QueryGen;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.util.ConvertUtil;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.core.util.SecurityUtil;
import com.kmxd.ams.infra.system.entity.*;
import com.kmxd.ams.infra.system.mapper.SysDeptMapper;
import com.kmxd.ams.infra.system.mapper.SysUserMapper;
import com.kmxd.ams.infra.system.mapper.SysUserPostMapper;
import com.kmxd.ams.infra.system.mapper.SysUserRoleMapper;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 用户表 服务实现类
 *
 * @author admin
 * @since 2021-06-10
 */
@Slf4j
@Service
@AllArgsConstructor
@CacheConfig(cacheNames = CacheConst.CACHE_PREFIX_SYS_USER)
public class SysUserSvcImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserSvc {

  private SysUserMapper sysUserMapper;
  private SysUserRoleMapper sysUserRoleMapper;
  private SysUserPostMapper sysUserPostMapper;
  private SysDeptMapper sysDeptMapper;
  private ISysDeptSvc sysDeptSvc;
  private ISysRoleSvc sysRoleSvc;
  private ISysFondsSvc sysFondsSvc;

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public Page<SysUserListVO> selectPage(Page reqPage, SysUserQueryDTO req) {
    List<SysDeptListVO> sysDeptList = new ArrayList();

    req.setDeptId(
        ObjectUtil.isNotEmpty(req.getDeptId())
            ? req.getDeptId()
            : SecurityUtil.getLoginUser().getDeptId());

    if (Objects.nonNull(req.getDeptId())) {
      List<SysDeptListVO> listAll = sysDeptSvc.listAll();
      sysDeptSvc.getFindInSet(sysDeptList, listAll, req.getDeptId());
    }
    List<String> sysDeptIdList =
        sysDeptList.stream().map(item -> String.valueOf(item.getId())).collect(Collectors.toList());

    LambdaQueryWrapper<SysUser> queryWrapper =
        QueryGen.init(new QueryWrapper<SysUser>(), req).lambda();
    queryWrapper.eq(SysUser::getStatus, StatusEnum.YES.getCode());
    if (ObjectUtil.isNotEmpty(req.getRoleId())) {
      List<SysUserRole> userLists =
          sysUserRoleMapper.selectList(
              Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getRoleId, req.getRoleId()));
      Set<Long> userIds =
          userLists.stream().map(SysUserRole::getUserId).collect(Collectors.toSet());
      if (userIds.isEmpty()) {
        return new PageDTO<>();
      }
      queryWrapper.in(ObjectUtil.isNotEmpty(userIds), SysUser::getId, userIds);
    }
    queryWrapper.and(
        Objects.nonNull(req.getDeptId()),
        wrapper ->
            wrapper
                .eq(SysUser::getDeptId, req.getDeptId())
                .or(
                    !CollectionUtils.isEmpty(sysDeptIdList),
                    wrapper2 -> wrapper2.in(SysUser::getDeptId, sysDeptIdList)));
    queryWrapper.orderByAsc(SysUser::getCreateTime);
    Page<SysUser> page = sysUserMapper.selectPage(reqPage, queryWrapper);
    Page<SysUserListVO> voPage = BeanUtil.copyProperties(page, Page.class);

    voPage.setRecords(BeanUtil.copyToList(page.getRecords(), SysUserListVO.class));
    List<Long> userIds =
        page.getRecords().stream().map(SysUser::getId).collect(Collectors.toList());
    return voPage;
  }

  @Override
  public Page<SysUserListVO> getUserListByRoleId(Page reqPage, Long roleId) {
    if (ObjectUtil.isEmpty(roleId)) {
      return new PageDTO<>();
    }
    List<SysUserRole> sysUserRoleList =
        sysUserRoleMapper.selectList(
            Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getRoleId, roleId));
    if (ObjectUtil.isEmpty(sysUserRoleList)) {
      return new PageDTO<>();
    }
    List<Long> roleIdList =
        sysUserRoleList.stream().map(item -> item.getUserId()).collect(Collectors.toList());
    Page<SysUser> page =
        this.sysUserMapper.selectPage(
            reqPage,
            Wrappers.lambdaQuery(SysUser.class)
                .in(SysUser::getId, roleIdList)
                .eq(SysUser::getStatus, StatusEnum.YES.getCode()));
    Page voPage = ConvertUtil.toBean(page, Page.class);
    voPage.setRecords(ConvertUtil.toList(page.getRecords(), SysUserListVO.class));
    return voPage;
  }

  @Override
  public Page<SysUserListVO> getUserListByFondsId(Page reqPage, Long fondsId) {
    if (ObjectUtil.isEmpty(fondsId)) {
      return new PageDTO<>();
    }
    SysFondsVO sysFondsVO = sysFondsSvc.view(fondsId);
    ISysCropSvc sysCropSvc = SpringUtil.getBean(ISysCropSvc.class);
    SysCropVO sysCropVO = sysCropSvc.view(sysFondsVO.getComId());
    String code = StrUtil.format(ROLE_SYS_ADMIN_CODE, sysCropVO.getId());
    SysRole sysRole = sysRoleSvc.selectByCode(code);
    if (ObjectUtil.isEmpty(sysRole)) {
      return new PageDTO<>();
    }
    return getUserListByRoleId(reqPage, sysRole.getId());
  }

  @Override
  public List<SysUserListVO> getUserListByRoleId(long roleId) {
    List<SysUserRole> sysUserRoleList =
        sysUserRoleMapper.selectList(
            Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getRoleId, roleId));
    if (ObjectUtil.isEmpty(sysUserRoleList)) {
      return Lists.newArrayList();
    }
    List<Long> roleIdList =
        sysUserRoleList.stream().map(item -> item.getUserId()).collect(Collectors.toList());
    List<SysUser> list =
        this.sysUserMapper.selectList(
            Wrappers.lambdaQuery(SysUser.class)
                .in(SysUser::getId, roleIdList)
                .eq(SysUser::getStatus, StatusEnum.YES.getCode()));
    return ConvertUtil.toList(list, SysUserListVO.class);
  }

  @Override
  public List<SysUserListVO> selectList(SysUserQueryDTO req) {
    if (ObjectUtil.isAllEmpty(req.getDeptId(), req.getRoleId())) {
      return Lists.newArrayList();
    }

    LambdaQueryWrapper<SysUser> queryWrapper =
        QueryGen.init(new QueryWrapper<SysUser>(), req).lambda();

    if (ObjectUtil.isNotEmpty(req.getKeywords())) {
      queryWrapper.and(
          (wrapper -> {
            wrapper.or(
                wrapper0 ->
                    wrapper0.like(SysUser::getAccount, StrUtil.format("{}%", req.getKeywords())));
            wrapper.or(
                wrapper2 ->
                    wrapper2.like(SysUser::getRealname, StrUtil.format("{}%", req.getKeywords())));
          }));
    }

    queryWrapper.eq(Objects.nonNull(req.getDeptId()), SysUser::getDeptId, req.getDeptId());
    queryWrapper.eq(SysUser::getStatus, StatusEnum.YES.getCode());
    queryWrapper.orderByAsc(SysUser::getCreateTime);

    List<SysUser> list = sysUserMapper.selectList(queryWrapper);
    List<SysUserListVO> voList = BeanUtil.copyToList(list, SysUserListVO.class);
    voList.forEach(
        item -> {
          SysDept sysDept = sysDeptMapper.selectById(item.getDeptId());
          item.setDeptName(Objects.isNull(sysDept) ? null : sysDept.getName());
        });
    return voList;
  }

  @Override
  public List<SysUserListVO> selectListByRoleOrDept(SysUserQueryDTO req) {
    if (ObjectUtil.isAllEmpty(req.getDeptId(), req.getRoleId())) {
      return Lists.newArrayList();
    }
    LambdaQueryWrapper<SysUserRole> wrapperUserRole = Wrappers.lambdaQuery();
    wrapperUserRole.eq(SysUserRole::getRoleId, req.getRoleId());
    List<SysUserRole> roleUsers = sysUserRoleMapper.selectList(wrapperUserRole);
    List<Long> userIds =
        roleUsers.stream().map(SysUserRole::getUserId).collect(Collectors.toList());
    List<SysUserListVO> users = selectListAll();
    return users.stream().filter(x -> userIds.contains(x.getId())).collect(Collectors.toList());
  }

  @Override
  public List<SysUserListVO> selectListByRoleType(SysUserRoleTypeQueryDTO req) {
    List<Integer> types = Lists.newArrayList();
    if (ObjectUtil.isEmpty(req.getRoleType())) {
      // 转指定用户
      // 如果是档案管理员，则可以选择多部门下的兼职人员
      // 如果是兼职人员，则可选择兼职人员及采集员
      if (ObjectUtil.equals(SecurityUtil.getRoleType(), DataScopeTypeEnum.FONDS.getType())) {
        types.add(DataScopeTypeEnum.DEPT.getType());
      } else if (ObjectUtil.equals(SecurityUtil.getRoleType(), DataScopeTypeEnum.DEPT.getType())) {
        types.add(DataScopeTypeEnum.DEPT.getType());
        types.add(DataScopeTypeEnum.USER.getType());
      } else {
        types.add(DataScopeTypeEnum.USER.getType());
      }
    } else {
      // 退回功能
      // 如果是档案管理员，则可以选择当前下的兼职人员及采集员
      // 如果是兼职人员，则可选择采集员
      if (ObjectUtil.equals(SecurityUtil.getRoleType(), DataScopeTypeEnum.FONDS.getType())) {
        types.add(DataScopeTypeEnum.DEPT.getType());
        types.add(DataScopeTypeEnum.USER.getType());
      } else {
        types.add(DataScopeTypeEnum.USER.getType());
      }
    }

    LambdaQueryWrapper<SysRole> wrapper = Wrappers.lambdaQuery();
    wrapper.in(!types.isEmpty(), SysRole::getRoleType, types);
    wrapper.eq(SysRole::getDataScope, "1");
    wrapper.eq(SysRole::getComId, SecurityUtil.getCropId());
    List<SysRole> roles = sysRoleSvc.list(wrapper);
    List<Long> roleIds = roles.stream().map(SysRole::getId).collect(Collectors.toList());
    if (roleIds.isEmpty()) {
      return Lists.newArrayList();
    }
    LambdaQueryWrapper<SysUserRole> wrapperUserRole = Wrappers.lambdaQuery();
    wrapperUserRole.in(SysUserRole::getRoleId, roleIds);
    List<SysUserRole> roleUsers = sysUserRoleMapper.selectList(wrapperUserRole);
    List<Long> userIds =
        roleUsers.stream().map(SysUserRole::getUserId).collect(Collectors.toList());
    List<SysUserListVO> users = selectListAll();
    return users.stream().filter(x -> userIds.contains(x.getId())).collect(Collectors.toList());
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<SysUserListVO> selectListAll() {
    return ConvertUtil.toList(this.list(), SysUserListVO.class);
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void add(SysUserAddDTO req) {
    if (!this.checkUniqueAccount(req.getAccount(), null)) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "账号已存在");
    }
    if (!this.checkUniquePhone(req.getPhone(), null)) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "手机号已存在");
    }
    SysUser entity = BeanUtil.copyProperties(req, SysUser.class);
    entity.setId(IdWorker.getId());
    entity.setDeptId(
        ObjectUtil.isNotEmpty(req.getDeptId())
            ? req.getDeptId()
            : SecurityUtil.getLoginUser().getDeptId());
    // 密码加密
    entity.setPassword(SecurityUtil.encryptPassword(entity.getPassword()));
    entity.setPswModified(YesNoEnum.NO.getCode());
    sysUserMapper.insert(entity);

    // 新增用户与角色关联
    if (Objects.nonNull(req.getRoleIds())) {
      // 删除用户与角色关联
      sysUserRoleMapper.delete(
          Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, entity.getId()));
      SysUserRole sysUserRole = new SysUserRole();
      sysUserRole.setUserId(entity.getId());
      sysUserRole.setRoleId(req.getRoleIds());
      sysUserRoleMapper.insert(sysUserRole);
    }
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void edit(SysUserEditDTO req) {
    if (!this.checkUniqueAccount(req.getAccount(), req.getId())) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "账号已存在");
    }
    if (!this.checkUniquePhone(req.getPhone(), req.getId())) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "手机号已存在");
    }
    // 当前用户不是超级管理员，不允许修改
    //    if (!SecurityUtil.getSysUser().getId().equals(req.getId())
    //        && SecurityUtil.isRootGroup(req.getId())) {
    //      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR,"超管账户不允许修改");
    //    }
    SysUser entity = BeanUtil.copyProperties(req, SysUser.class);
    sysUserMapper.updateById(entity);

    // 新增用户与角色关联
    if (Objects.nonNull(req.getRoleIds())) {

      // 删除用户与角色关联
      sysUserRoleMapper.delete(
          Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, entity.getId()));
      SysUserRole sysUserRole = new SysUserRole();
      sysUserRole.setUserId(entity.getId());
      sysUserRole.setRoleId(req.getRoleIds());
      sysUserRoleMapper.insert(sysUserRole);
    }

    //编辑受用
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void remove(String ids) {
    Set<Long> idList = IdUtil.split(ids);
    idList.forEach(
        item -> {
          if (SecurityUtil.isRootGroup(item)) {
            throw BizTipException.instance(ErrorCode.EXCEPTION, "超管账户不允许删除");
          }
          SysUser sysUserTemp = new SysUser();
          sysUserTemp.setId(item);
          sysUserTemp.setDelFlag(DelFlagEnum.YES.getCode());
          sysUserMapper.deleteById(sysUserTemp);
        });
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public SysUserVO view(Long id) {
    if (ObjectUtil.isEmpty(id)) {
      return null;
    }
    SysUser entity = sysUserMapper.selectById(id);
    SysUserVO sysUserVO = BeanUtil.copyProperties(entity, SysUserVO.class);

    return sysUserVO;
  }

  @CacheEvict(allEntries = true)
  @Override
  public void resetPwd(SysUserResetPwdDTO req) {
    SysUser entity = BeanUtil.copyProperties(req, SysUser.class);
    // 密码加密
    entity.setPassword(SecurityUtil.encryptPassword(entity.getPassword()));
    entity.setPswModified(YesNoEnum.YES.getCode());
    sysUserMapper.updateById(entity);
  }

  @Override
  public LoginUser getUserInfo() {
    return SecurityUtil.getLoginUser();
  }

  @Override
  public Set<String> getPermCode() {
    return SecurityUtil.getLoginUser().getPermissions();
  }

  @Override
  public AccountInfoVO getAccountInfo() {
    LoginUser loginUser = SecurityUtil.getLoginUser();
    SysUser sysUser = sysUserMapper.selectById(loginUser.getSysUser().getId());
    return BeanUtil.copyProperties(sysUser, AccountInfoVO.class);
  }

  @CacheEvict(allEntries = true)
  @Override
  public void saveAccountInfo(AccountInfoVO req) {
    LoginUser loginUser = SecurityUtil.getLoginUser();
    SysUser entity = BeanUtil.copyProperties(req, SysUser.class);
    entity.setId(loginUser.getSysUser().getId());
    sysUserMapper.updateById(entity);
  }

  @Override
  public Boolean checkUniqueAccount(String value, Long id) {
    if (StrUtil.isBlank(value)) {
      return true;
    }
    id = Objects.isNull(id) ? -1L : id;
    LambdaQueryWrapper<SysUser> queryWrapper =
        Wrappers.lambdaQuery(SysUser.class)
            .select(SysUser::getId, SysUser::getAccount)
            .eq(SysUser::getAccount, value);
    List<SysUser> lists = sysUserMapper.selectList(queryWrapper);
    SysUser sysUser = lists.stream().findFirst().orElse(null);
    return Objects.isNull(sysUser) || NumberUtil.equals(sysUser.getId(), id);
  }

  private Boolean checkUniqueEmail(String value, Long id) {
    if (StrUtil.isBlank(value)) {
      return true;
    }
    id = Objects.isNull(id) ? -1L : id;

    LambdaQueryWrapper<SysUser> queryWrapper =
        Wrappers.lambdaQuery(SysUser.class)
            .select(SysUser::getId, SysUser::getEmail)
            .eq(SysUser::getEmail, value);

    List<SysUser> lists = sysUserMapper.selectList(queryWrapper);
    SysUser sysUser = lists.stream().findFirst().orElse(null);
    return Objects.isNull(sysUser) || NumberUtil.equals(sysUser.getId(), id);
  }

  private Boolean checkUniquePhone(String value, Long id) {
    if (StrUtil.isBlank(value)) {
      return true;
    }
    id = Objects.isNull(id) ? -1L : id;
    LambdaQueryWrapper<SysUser> queryWrapper =
        Wrappers.lambdaQuery(SysUser.class)
            .select(SysUser::getId, SysUser::getPhone)
            .eq(SysUser::getPhone, value);

    List<SysUser> lists = sysUserMapper.selectList(queryWrapper);
    SysUser sysUser = lists.stream().findFirst().orElse(null);
    return Objects.isNull(sysUser) || NumberUtil.equals(sysUser.getId(), id);
  }

  @Override
  public SysUserVO getUserByAccount(String account) {
    SysUser one = getOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getAccount, account));
    return BeanUtil.copyProperties(one, SysUserVO.class);
  }

  @CacheEvict(allEntries = true)
  @Override
  public void clearAllCache() {
    log.info("清除系统参数缓存");
  }
}
